#include<iostream>

#include<stdio.h>

#include<string>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include<string.h>

#include<unistd.h>

#include<vector>

#include<unordered_map>

#define SIZE 1024

std::string defaultip = "0.0.0.0";
uint16_t defaultport = 8080;

const int backlog = 1;

enum
{
    SOCKETERR = 1,
    BINDERR,
    lISTENERR
};

class tcpserver;

struct task
{
    int skfd;
    sockaddr_in in;
    tcpserver* THIS;
};

class tcpserver
{
public:
    tcpserver(const std::string& ip = defaultip, uint16_t port = defaultport):
        _ip(defaultip),
        _port(port),
        _sockfd(0),
        isrunning(false)
    {
    }

    void init()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            perror("socket");
            exit(SOCKETERR);
        }

        sockaddr_in in;
        bzero(&in, sizeof(in));
        in.sin_addr.s_addr = inet_addr(_ip.c_str());
        in.sin_family = AF_INET;
        in.sin_port = htons(_port);

        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        if(bind(_sockfd, (const sockaddr*)&in, sizeof(in)) < 0)
        {
            perror("bind");
            exit(BINDERR);
        }
        std::cout << "bind success" << std::endl;

        if(listen(_sockfd, 2) < 0)
        {
            perror("listen");
            exit(lISTENERR);
        }

        std::cout << "listen success" << std::endl;
    }

    void Broadcast(const std::string& message)
    {
        for(auto& e : _um)
        {
            write(e.second->skfd, message.c_str(), message.size());
        }
    }

    static void* service(void* arg)
    {
        pthread_detach(pthread_self());
        // std::cout << 1 << std::endl;
        task* tkptr = static_cast<task*>(arg);
        char buffer[SIZE] = {0};
        // while(1)
        // {
        //     ssize_t n = read(tkptr->skfd, buffer, SIZE);
        //     if(n > 0)
        //     {
        //         buffer[n] = 0;
        //         std::string ip = inet_ntoa(tkptr->in.sin_addr);
        //         std::string message = "[" + ip + ":" + std::to_string((int)ntohs(tkptr->in.sin_port)) + "]" + buffer;
        //         // std::cout << "[" << ip << ":" << tkptr->in.sin_port << "]" << buffer << std::endl;

        //         std::cout << message << std::endl;
        //         tkptr->THIS->Broadcast(message);
        //     }
        //     else if(n == 0)
        //     {
        //         sleep(3);
        //         close(tkptr->skfd);
        //         break;
        //     }
        // }
        

        close(tkptr->skfd);

        delete tkptr;

        return nullptr;
    }

    void check_user(task*& tkptr)
    {
        std::string strid = inet_ntoa(tkptr->in.sin_addr);
        int intport = tkptr->in.sin_port;
        strid += ":" + std::to_string(intport);
        if(_um.count(strid) == 0)
        {
            _um[strid] = tkptr;
            std::cout << "add a new user：" << strid << std::endl;
        }
    }

    void run()
    {
        isrunning = true;
        char buffer[SIZE] = {0};
        std::string message;
        std::vector<pthread_t>add;
        while(isrunning)
        {
            task* tkptr = new task;
            tkptr->THIS = this;
            socklen_t inlen = sizeof(tkptr->in);
            tkptr->skfd = accept(_sockfd, (sockaddr*)&(tkptr->in), &inlen);

            check_user(tkptr);

            pthread_t PT;
            // service(tkptr);
            pthread_create(&PT, nullptr, service, (void*)tkptr);
        }
    }

    ~tcpserver()
    {
        close(_sockfd);
    }
private:
    std::string _ip;
    uint16_t _port;
    int _sockfd;
    bool isrunning;
    std::unordered_map<std::string, task*>_um;
};